#!/usr/bin/env python

import os
import wx
import numpy
import thread
import dialogs
import datetime
import components
import presenters
import interactors
import louise.measure

from _base import Base
from resources import icons

class Measure(Base):
    
    def __init__(self, view, interactor):
        super(Measure, self).__init__(view, interactor)
        self.view.Title = 'Measure'
        self.view.SetTransparent(240)        
        self.view.Icon = icons.Measure.Icon
        config = louise.measure.Config()
        self.restoreSize(config)
        self.restorePosition(config)
        self.restoreSetting(config)
    
    def onClose(self):
        config = louise.measure.Config()
        self.saveSetting(config)
        self.savePosition(config)
        self.saveSize(config)
    
    def restoreSetting(self, config):
        view = self.view
        view.location.Value = config.Read('Location/Name', '')
        view.saveTo.Label = config.Read('Location/Folder', '')
        view.length.Value = config.Read('Data/Length', '1.0')
        view.stack.Value = config.ReadInt('Data/Stack', 1)
        view.trigger.Value = config.Read('Trigger/Type', 'Trigger 1')
        view.sensitivity.Value = config.ReadInt('Trigger/Sensitivity', 1)
    
    def saveSetting(self, config):
        view = self.view
        config.Write('Location/Name', view.location.Value)
        config.Write('Location/Folder', view.saveTo.Label)
        config.Write('Data/Length', view.length.Value)
        config.WriteInt('Data/Stack', view.stack.Value)
        config.Write('Trigger/Type', view.trigger.Value)
        config.WriteInt('Trigger/Sensitivity', view.sensitivity.Value)
        
    def showSetting(self):
        setting = dialogs.Setting(self.view)
        config = louise.measure.Config()
        setting.device = config.Read('Device/Name', 'Dev1')
        setting.frequency = config.Read('Device/Frequency', '.125 ms')
        for index in range(1, 25):
            gain = config.ReadInt('Gain/%s' % index, 5)
            setting.channels[index].gain = gain
        if setting.ShowModal() == wx.ID_OK:
            config.Write('Device/Name', setting.device)
            config.Write('Device/Frequency', setting.frequency)
            for index in range(1, 25):
                config.WriteInt('Gain/%s' % index, setting.channels[index].gain)
        setting.Destroy()
        
    def start(self):
        config = louise.measure.Config()
        self.saveSetting(config)
        self._canceled = False
        view = self.view
        view.start()
        if view.trigger.Value == 'Trigger 2':
            thread.start_new_thread(self.startDigital, (config,))
        else:
            thread.start_new_thread(self.startAnalog, (config,))
    
    def stop(self):
        view = self.view
        view.stop()
        view.status = ''
        self._canceled = True
    
    _canceled = False
    
    def startAnalog(self, config):
        import nidaqmx
    
        device = config.Read('Device/Name', 'Dev1')
        sensitivity = 11 - config.ReadInt('Trigger/Sensitivity', 1)
        frequency = config.Read('Device/Frequency', '.125 ms')
        rate = components.Nidaqmx.calcRate(frequency)
        stack = config.ReadInt('Data/Stack', 1)
        sample = int(float(config.Read('Data/Length')) * rate)
        
        task = nidaqmx.AnalogInputTask()
        
        for channel in range(24):
            gain = config.ReadInt('Gain/%s' % (channel + 1), 1)
            max = 11 - gain
            task.create_voltage_channel('%s/ai%s' % (device, channel),
                                        terminal='rse', min_val=-max,
                                        max_val=max, units='volts')
        
        task.create_voltage_channel('%s/ai24' % device, terminal='rse',
                                    min_val=-sensitivity,
                                    max_val=sensitivity,
                                    units='volts')
        task.configure_timing_sample_clock(rate=rate, samples_per_channel=1)
        
        hasData = False
        data = numpy.zeros((sample, 24))
        
        task.start()
        
        for i in range(stack):
            wx.CallAfter(self.view.idle)
            message = 'Waiting for trigger %s of %s...' % (i + 1, stack)
            wx.CallAfter(self.view.setStatus, message)
            triggered = False
            while True:
                datum = task.read(1, fill_mode='group_by_scan_number')[0]
                value = datum[24]
                if value < -sensitivity or value > sensitivity: triggered = True
                if triggered or self._canceled: break
            
            if triggered and not self._canceled:
                wx.CallAfter(self.view.setStatus, 'Reading data...')
                wx.CallAfter(self.view.busyInfo, 'Reading data...')
                for j in range(24):
                    data[0][j] += datum[j]
                for k in range(1, sample):
                    datum = task.read(1, fill_mode='group_by_scan_number')[0]
                    for j in range(24):
                        data[k][j] += datum[j]
                hasData = True
            
            if self._canceled: break
            
        task.stop()
        
        if hasData and not self._canceled:
            wx.CallAfter(self.view.setStatus, 'Saving data...')
            wx.CallAfter(self.view.busyInfo, 'Saving data...')
            self.save(data, config)
            wx.CallAfter(self.view.idle)
            wx.CallAfter(self.stop)
            wx.CallAfter(self.view.setStatus, 'Data saved successfuly.')
        
    def startDigital(self, config):
        import nidaqmx
        
        device = config.Read('Device/Name', 'Dev1')
        sensitivity = config.ReadInt('Trigger/Sensitivity', 1)
        frequency = config.Read('Device/Frequency', '.125 ms')
        rate = components.Nidaqmx.calcRate(frequency)
        stack = config.ReadInt('Data/Stack', 1)
        sample = int(float(config.Read('Data/Length')) * rate)
        
        task = nidaqmx.AnalogInputTask()
        
        for channel in range(24):
            gain = config.ReadInt('Gain/%s' % (channel + 1), 1)
            max = 11 - gain
            task.create_voltage_channel('%s/ai%s' % (device, channel),
                                        terminal='rse', min_val=-max,
                                        max_val=max, units='volts')
        
        task.configure_timing_sample_clock(rate=rate, samples_per_channel=1)
        
        trigger = nidaqmx.DigitalInputTask()
        
        trigger.create_channel('%s/port0/line7' % device,
                               grouping='for_all_lines')
        
        hasData = False
        data = numpy.zeros((sample, 24))
        
        for i in range(stack):
            wx.CallAfter(self.view.idle)
            message = 'Waiting for trigger %s of %s...' % (i + 1, stack)
            wx.CallAfter(self.view.setStatus, message)
            trigger.start()
            trigerred = False
            
            while True:
                datum, count = trigger.read(1, fill_mode='group_by_channel')
                if datum[0][0] == 1:
                    task.start()
                    trigger.stop()
                    trigerred = True
                if trigerred or self._canceled: break
            
            if trigerred and not self._canceled:
                wx.CallAfter(self.view.setStatus, 'Reading data...')
                wx.CallAfter(self.view.busyInfo, 'Reading data...')
                for k in range(sample):
                    datum = task.read(1, fill_mode='group_by_scan_number')[0]
                    for j in range(24):
                        data[k][j] = data[k][j] + datum[j]
                hasData = True
                
                task.stop()
            
            if self._canceled: break
        
        if hasData and not self._canceled:
            wx.CallAfter(self.view.setStatus, 'Saving data...')
            wx.CallAfter(self.view.busyInfo, 'Saving data...')
            self.save(data, config)
            wx.CallAfter(self.view.idle)
            wx.CallAfter(self.stop)
            wx.CallAfter(self.view.setStatus, 'Data saved successfuly.')
        
    def save(self, data, config):
        folder = config.Read('Location/Folder', '')
        name = config.Read('Location/Name', '')
        now = datetime.datetime.now()
        filename = '%s [%s].txt' % (name, now.strftime('%Y-%m-%d %H-%M-%S'))
        path = os.path.join(folder, filename)
        file = open(path, 'a')
        write = file.write
        write('Location: %s\n' % name)
        write('Time: %s\n' % now.strftime('%Y-%m-%d %H:%M:%S'))
        frequency = config.Read('Device/Frequency', '.125 ms')
        rate = components.Nidaqmx.calcRate(frequency)
        samplingRate = 1000 / rate
        write('Sampling Rate (ms): %s\n' % samplingRate)
        sample = int(float(config.Read('Data/Length')) * rate)
        write('Sample Number: %s\n' % sample)
        stack = config.ReadInt('Data/Stack', 1)
        write('Stack Count: %s\n\n' % stack)
        channel = ''
        gain = ''
        min = ''
        max = ''
        maxs = {}
        for i in range(1, 25):
            channel += '\t%s' % '{0: >5}'.format(i)
            g = config.ReadInt('Gain/%s' % i, 1)
            gain += '\t%s' % '{0: >5}'.format(g)
            m = 11 - g
            maxs[i - 1] = m
            min += '\t%s' % '{0: >5}'.format(-m)
            max += '\t%s' % '{0: >5}'.format(m)
        write('Chan:%s\n' % channel)
        write('Gain:%s\n' % gain)
        write('Min:%s\n' % min)
        write('Max:%s\n' % max)
        
        for row in range(int(sample)):
            datum = ''
            for col in range(24):
                value = data[row][col]
                max = float(maxs[col])
                l = int(32767 * (value / max))
                if l > 32767: l = 32767
                elif l < -32767: l = -32767
                datum += '\t%s' % '{0: >5}'.format(l)
            write('Data:%s\n' % datum)
            
        file.close()
        
        # Create SegY file
        segy = components.SegY(path)
        segy.save()
        